//
//  ThreadPool.h
//
//  Created by 周俊 on 14/12/10.
//
//

#ifndef __ThreadPool__Header__
#define __ThreadPool__Header__

#include "Macro.h"
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <atomic>
#include <future>

TSP_NS_START

class ThreadPool
{
public:
    ThreadPool(size_t size);
    ~ThreadPool();

    template<class F, class... Args>
    std::future<typename std::result_of<F(Args...)>::type> enqueue(F&& f, Args&&... args);

    void reset();

    inline unsigned int getMaxTaskNum()
    {
        return m_allTaskNum;
    }

    inline unsigned int getCurrentFinishedNum()
    {
        return m_finishedTaskNum;
    }

    inline unsigned int getNotCompletedTaskNum()
    {
        return m_allTaskNum - m_finishedTaskNum;
    }

    inline unsigned int getProgressPercentage()
    {
        return m_finishedTaskNum * 100 / m_allTaskNum;
    }

    inline unsigned int getRunningTaskNum()
    {
        return m_allTaskNum - m_tasks.size() - m_finishedTaskNum;
    }

private:
    //用来保存worker线程
    std::vector<std::thread> m_workers;
    
    //任务队列
    std::queue<std::function<void()>> m_tasks;

    //用来处理同步
    std::mutex m_queueMutex;
    std::condition_variable m_condition;
    bool m_stop;

    //用来计数
    std::atomic<unsigned int> m_finishedTaskNum;
    std::atomic<unsigned int> m_allTaskNum;

    TSP_DISALLOW_COPY_AND_ASSIGN(ThreadPool)
};

template<class F, class... Args>
std::future<typename std::result_of<F(Args...)>::type> ThreadPool::enqueue(F&& f, Args&&... args)
{
    using returnType = typename std::result_of<F(Args...)>::type;
    auto task = std::make_shared<std::packaged_task<returnType()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
    {
        std::unique_lock<std::mutex> lock(m_queueMutex);
        // don't allow enqueueing after stopping the pool
        if(this->m_stop)
        {
            throw std::runtime_error("enqueue on stopped ThreadPool");
        }
        m_allTaskNum += 1;
        m_tasks.emplace([task]()
        {
            (*task)();
        });
    }
    this->m_condition.notify_one();
    return task->get_future();
}


TSP_NS_END

#endif /* defined(__smallworld__ThreadPool__) */
